/* $Id: RejudgeModel.java 137 2011-08-29 20:33:36Z altdotua@gmail.com $ */

package alt.djudge.frontend.server.models.scores;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jdo.PersistenceManager;

import alt.djudge.frontend.server.Deployment;
import alt.djudge.frontend.server.PMF;
import alt.djudge.frontend.server.datatypes.ContestEntry;
import alt.djudge.frontend.server.datatypes.ContestProblemEntry;
import alt.djudge.frontend.server.datatypes.SubmissionEntry;
import alt.djudge.frontend.server.datatypes.scores.ContestProblemScoreEntry;
import alt.djudge.frontend.server.datatypes.scores.ContestScoreEntry;
import alt.djudge.frontend.server.datatypes.scores.ContestUserScoreEntry;
import alt.djudge.frontend.server.datatypes.scores.ProblemUserScoreEntry;
import alt.djudge.frontend.server.datatypes.scores.UserScoreEntry;
import alt.djudge.frontend.server.models.CacheModel;
import alt.djudge.frontend.server.models.ContestProblemsModel;
import alt.djudge.frontend.server.models.ContestsModel;
import alt.djudge.frontend.server.models.SubmissionsModel;

abstract class MyMap<K, T>
{
	List<T> list;
	
	Map<K, T> map = new HashMap<K, T>();
	
	abstract K getKey(T value);
	
	public MyMap(List<T> items)
	{
		list = items;
		for (T item : list)
			map.put(getKey(item), item);
	}
	
	public T get(K key)
	{
		T val = map.get(key);
		if (val == null)
		{
			val = create(key);
			list.add(val);
			map.put(key, val);
			System.out.println("Map dump");
			for (K tkey : map.keySet())
			{
				System.out.println(tkey.toString() + " => " + map.get(key));
			}
		}
		return val;
	}
	
	protected abstract T create(K key);
}

class Tuple
{
	long first, second;
	
	public Tuple(Long first, Long second)
	{
		this.first = first;
		this.second = second;
	}
	
	public boolean equals(Object o)
	{
		if (o instanceof Tuple)
		{
			Tuple t = (Tuple) o;
			return t.first == first && t.second == second;
		}
		return super.equals(o);
	}
	
	@Override
	public int hashCode()
	{
		return (int)(first * second);
	}
	
	@Override
	public String toString()
	{
		return "[" + first + ", " + second + "]";
	}
}

public class RejudgeModel
{
	public static void deleteStats()
	{
		ContestProblemScoreModel.deleteAllEntries();
		ContestScoreModel.deleteAllEntries();
		ContestUserScoreModel.deleteAllEntries();
		ProblemUserScoreModel.deleteAllEntries();
		UserScoreModel.deleteAllEntries();
	}
	
	// TODO: protect by transaction
	public static void rejudgeAllHardVirtual()
	{
		Deployment.setLockJudge(true);
		CacheModel.clearCache();
		deleteStats();
		CacheModel.clearCache();
		
		List<UserScoreEntry> listUserScoreEntries = new ArrayList<UserScoreEntry>(UserScoreModel.getAllEntries());
		MyMap<Long, UserScoreEntry> mapUserScores = new MyMap<Long, UserScoreEntry>(listUserScoreEntries)
		{

			@Override
			protected UserScoreEntry create(Long key)
			{
				return new UserScoreEntry(key);
			}

			@Override
			Long getKey(UserScoreEntry value)
			{
				return value.getUserId();
			}
		};

		List<ContestScoreEntry> listContestScoreEntries = new ArrayList<ContestScoreEntry>(ContestScoreModel.getAllEntries());
		MyMap<Long, ContestScoreEntry> mapContestScores = new MyMap<Long, ContestScoreEntry>(listContestScoreEntries)
		{

			@Override
			protected ContestScoreEntry create(Long key)
			{
				return new ContestScoreEntry(key);
			}

			@Override
			Long getKey(ContestScoreEntry value)
			{
				return value.getContestId();
			}
		};
		
		List<ContestProblemScoreEntry> listContestProblemScoreEntries = new ArrayList<ContestProblemScoreEntry>(ContestProblemScoreModel.getAllEntries());
		MyMap<Tuple, ContestProblemScoreEntry> mapContestProblemScores = new MyMap<Tuple, ContestProblemScoreEntry>(listContestProblemScoreEntries)
		{

			@Override
			protected ContestProblemScoreEntry create(Tuple key)
			{
				return new ContestProblemScoreEntry(key.first, key.second);
			}

			@Override
			Tuple getKey(ContestProblemScoreEntry value)
			{
				return new Tuple(value.getContestId(), value.getContestProblemId());
			}
		};
		
		List<ContestUserScoreEntry> listContestUserScoreEntries = new ArrayList<ContestUserScoreEntry>(ContestUserScoreModel.getAllEntries());
		MyMap<Tuple, ContestUserScoreEntry> mapContestUserScores = new MyMap<Tuple, ContestUserScoreEntry>(listContestUserScoreEntries)
		{

			@Override
			protected ContestUserScoreEntry create(Tuple key)
			{		
				return new ContestUserScoreEntry(key.first, key.second);
			}

			@Override
			Tuple getKey(ContestUserScoreEntry value)
			{
				return new Tuple(value.getContestId(), value.getUserId());
			}
		};
		
		List<ProblemUserScoreEntry> listProblemUserScoreEntries = new ArrayList<ProblemUserScoreEntry>(ProblemUserScoreModel.getAllEntries());
		MyMap<Tuple, ProblemUserScoreEntry> mapProblemUserScores = new MyMap<Tuple, ProblemUserScoreEntry>(listProblemUserScoreEntries)
		{

			@Override
			protected ProblemUserScoreEntry create(Tuple key)
			{
				return new ProblemUserScoreEntry(key.first, key.second);
			}

			@Override
			Tuple getKey(ProblemUserScoreEntry value)
			{				
				return new Tuple(value.getContestProblemId(), value.getUserId());
			}
		};
		
		List<SubmissionEntry> list = SubmissionsModel.getAllSubmissions();
		for (int i = list.size() - 1; i >= 0; i--)
		{
			SubmissionEntry se = list.get(i);
			if (se.getActive())
			{
				se.addScores(
						mapUserScores.get(se.getUserId()), 
						mapContestScores.get(se.getContestId()),
						mapContestProblemScores.get(new Tuple(se.getContestId(), se.getContestProblemId())),
						mapContestUserScores.get(new Tuple(se.getContestId(), se.getUserId())),
						mapProblemUserScores.get(new Tuple(se.getContestProblemId(), se.getUserId())), false
						);
			}
		}
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try
		{
			pm.makePersistentAll(listContestProblemScoreEntries);
			pm.makePersistentAll(listContestScoreEntries);
			pm.makePersistentAll(listContestUserScoreEntries);
			pm.makePersistentAll(listProblemUserScoreEntries);
			pm.makePersistentAll(listUserScoreEntries);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			pm.close();
		}
		
		CacheModel.clearCache();
		
		Deployment.setLockJudge(false);
	}
	
	public static void rejudgeAllHardReal()
	{
		Deployment.setLockJudge(true);
		UserScoreModel.deleteAllEntries();
		ContestScoreModel.deleteAllEntries();
		ContestProblemScoreModel.deleteAllEntries();
		ContestUserScoreModel.deleteAllEntries();
		ProblemUserScoreModel.deleteAllEntries();
		
		List<SubmissionEntry> list = SubmissionsModel.getAllSubmissions();
		for (int i = list.size() - 1; i >= 0; i--)
		{
			SubmissionEntry se = list.get(i);
			if (se.getActive())
			{
				se.incrementPendingsCount();
				se.prepareForRejudge();
				se.makePersistent();
			}
		}
		
		Deployment.setLockJudge(false);
	}
	
	public static void rejudgeAll(boolean fVirualRejudge)
	{
		List<ContestEntry> contests = ContestsModel.getAllEntries();
		for (ContestEntry contestEntry : contests)
		{
			rejudgeContest(contestEntry.getId(), fVirualRejudge);
		}
	}
	
	public static void rejudgeContest(Long contestId,  boolean fVirtualRejudge)
	{
		Deployment.setLockJudge(true);
		List<ContestProblemEntry> problemEntries = ContestProblemsModel.getContestEntries(contestId);
		for (ContestProblemEntry problemEntry : problemEntries)
		{
			rejudgeProblem(problemEntry.getContestId(), fVirtualRejudge);
		}
		Deployment.setLockJudge(false);
	}
	
	public static void rejudgeProblem(Long contestProblemId, boolean fVirtualRejudge)
	{
		Deployment.setLockJudge(true);
		
		Deployment.setLockJudge(false);
	}
}
